package org.jetlinks.pro.auth.web;

import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.exception.AccessDenyException;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.jetlinks.pro.auth.entity.TenantMemberEntity;
import org.jetlinks.pro.auth.service.TenantMemberService;
import org.jetlinks.pro.auth.service.request.BindMemberRequest;
import org.jetlinks.pro.tenant.TenantMember;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@AllArgsConstructor
public class WebFluxUserController extends org.hswebframework.web.system.authorization.defaults.webflux.WebFluxUserController {

    private final TenantMemberService memberService;

    @Override
    public Flux<UserEntity> query(@Parameter(hidden = true) @RequestBody Mono<QueryParamEntity> query) {
        return super.query(query.flatMap(param -> TenantMember.injectMemberQueryParam(param, "id")));
    }

    @Override
    public Flux<UserEntity> query(@Parameter(hidden = true) QueryParamEntity query) {
        return TenantMember
            .injectMemberQueryParam(query, "id")
            .flatMapMany(super::query);
    }

    @Override
    public Mono<PagerResult<UserEntity>> queryPager(@Parameter(hidden = true) @RequestBody Mono<QueryParamEntity> query) {
        return super.queryPager(query.flatMap(param -> TenantMember.injectMemberQueryParam(param, "id")));
    }

    @Override
    public Mono<PagerResult<UserEntity>> queryPager(@Parameter(hidden = true) QueryParamEntity query) {
        return TenantMember
            .injectMemberQueryParam(query, "id")
            .flatMap(super::queryPager);
    }

    @Override
    public Mono<Integer> count(@Parameter(hidden = true) @RequestBody Mono<QueryParamEntity> query) {
        return super.count(query.flatMap(param -> TenantMember.injectMemberQueryParam(param, "id")));
    }

    @Override
    public Mono<Integer> count(@Parameter(hidden = true) QueryParamEntity query) {
        return TenantMember
            .injectMemberQueryParam(query, "id")
            .flatMap(super::count);
    }

    @Override
    public Mono<Boolean> saveUser(@RequestBody Mono<UserEntity> userMono) {
        return userMono
            .flatMap(user -> {
                Mono<Void> before;
                boolean isNew = StringUtils.isEmpty(user.getId());
                if (!isNew) {
                    //如果不是新创建用户,则判断是否有权限修改
                    before = assertUserPermission(user.getId());
                } else {
                    before = Mono.empty();
                }
                return before
                    .then(super.saveUser(Mono.just(user)))
                    .then(isNew ?
                              //绑定新创建的用户到当前租户
                              TenantMember
                                  .current()
                                  .flatMap(member -> memberService
                                      .bindMembers(member.getTenant().getId(),
                                                   Flux.just(new BindMemberRequest(user.getName(),
                                                                                   user.getId(),
                                                                                   null,
                                                                                   false,
                                                                                   null)
                                                   )))
                                  .then()
                              : Mono.empty()
                    )
                    .thenReturn(true);
            });
    }

    @Override
    public Mono<Boolean> deleteUser(@PathVariable String id) {
        return assertUserPermission(id)
            .then(super.deleteUser(id))
            ;
    }

    @Override
    public Mono<Integer> changeState(@PathVariable @Parameter(description = "用户ID") String id,
                                     @PathVariable @Parameter(description = "状态,0禁用,1启用") Byte state) {
        return assertUserPermission(id)
            .then(
                super.changeState(id, state)
            );
    }

    @Override
    public Mono<UserEntity> getById(@PathVariable String id) {
        return assertUserPermission(id)
            .then(super.getById(id));
    }

    private Mono<Void> assertUserPermission(String userId) {
        return TenantMember
            .currentAll()
            .map(mem -> mem.getTenant().getId())
            .collectList()
            .filter(CollectionUtils::isNotEmpty)
            .flatMap(tenantIds -> memberService
                .createQuery()
                .where(TenantMemberEntity::getUserId, userId)
                .in(TenantMemberEntity::getTenantId, tenantIds)
                .fetch()
                .switchIfEmpty(Mono.error(AccessDenyException::new))
                .then());
    }
}
